package org.lex.explorer.directory;

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.lex.explorer.AbstractExplorer;
import org.lex.swing.Swings;
import org.lex.utils.SystemProperties;

public class DirectoryExplorer extends AbstractExplorer {
	private JScrollPane scrollPane;
	private JTree tree;
	private FileTreeRenderer treeRenderer = null;
	private File directory = null;

	public static void main(String[] args) {
		Swings.setUseBoldMetalFont(false);
		Swings.show(new DirectoryExplorer(), false);
	}

	public DirectoryExplorer() {
		this(new File(SystemProperties.userHome()));
	}

	public DirectoryExplorer(File directory) {
		super();
		this.directory = directory;
		initialize();
	}

	@Override
	public File getDirectory() {
		return this.directory;
	}

	@Override
	public void setDirectory(File directory, boolean fireEvent) {
		log.trace("change to directory: " + directory.toString());
		File old = this.directory;
		this.findFile(directory);
		if (true == fireEvent)
			this.fireDirectoryChange(old, directory);
	}

	private void initialize() {
		this.setLayout(new BorderLayout());
		this.add(getScrollPane(), BorderLayout.CENTER);
		this.findFile(this.directory);
	}

	private JScrollPane getScrollPane() {
		if (scrollPane == null) {
			scrollPane = new JScrollPane();
			scrollPane.setViewportView(getTree());
		}
		return scrollPane;
	}

	private JTree getTree() {
		if (tree == null) {
			tree = new JTree();
			tree.setToggleClickCount(1);
			tree.setRootVisible(false);
			tree.setCellRenderer(this.getTreeRenderer());
			tree.setModel(new FileSystemTreeModel());
			tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
			// tree selection listener
			tree.addMouseListener(new MouseAdapter() {
				public void mousePressed(MouseEvent e) {
					if (MouseEvent.BUTTON1 != e.getButton())
						return;
					TreePath path = tree.getPathForLocation(e.getX(), e.getY());
					if (null == path)
						return;
					File dir = ((FileNode) path.getLastPathComponent()).getFile();
					File old = directory;
					directory = dir;
					fireDirectoryChange(old, dir);
				}
			});
		}
		return tree;
	}

	private FileTreeRenderer getTreeRenderer() {
		if (null == treeRenderer) {
			this.treeRenderer = new FileTreeRenderer();
		}
		return this.treeRenderer;
	}

	/**
	 * Ensures the file is visible
	 * 
	 * @param fileToLocate
	 * @param selectFile
	 */
	private void findFile(File file) {
		if (file == null || false == file.exists() || false == file.isDirectory())
			return;
		try {
			file = file.getCanonicalFile();
		} catch (Exception e) {
			return;
		}
		// split the full path into individual files to
		// locate them in the tree model
		FileSystemView fsv = FileSystemView.getFileSystemView();
		List<File> filePath = new ArrayList<File>();
		while (null != file) {
			filePath.add(file);
			file = fsv.getParentDirectory(file);
		}
		Collections.reverse(filePath);
		// start from the root
		List<TreeNode> nodePath = new ArrayList<TreeNode>();
		TreeNode root = (TreeNode) getTree().getModel().getRoot();
		nodePath.add(root);
		// ...and go through the tree model to find the files. Stop as
		// soon as path is completely found or if one of the files in the path
		// is not found.
		boolean found = true;
		TreeNode current = null;
		while (filePath.size() > 0 && found) {
			found = false;
			for (int i = 0, c = root.getChildCount(); i < c; i++) {
				current = root.getChildAt(i);
				File f = ((FileNode) current).getFile();
				if (filePath.get(0).equals(f)) {
					nodePath.add(current);
					filePath.remove(0);
					root = current;
					found = true;
					break;
				}
			}
		}
		// select the path we found, it may be the file we were looking
		// for or a subpath only
		TreePath path = new TreePath(nodePath.toArray());
		tree.setSelectionPath(path);
		tree.expandPath(path);
		tree.scrollPathToVisible(path);
	}
}
